﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace iTunesUpdater
{
  public class TitleScanner
  {
    public TitleScanner(string Patterns) {
      // separate our list of patterns (one pattern per line) in to a list
      string[] patterns = Patterns.Split(null);
    }

    List<SearchPattern> SearchPatterns = new List<SearchPattern>();
    public TitleScanner(System.Collections.IList Patterns) {
      foreach (string pattern in Patterns) {
        SearchPatterns.Add(new SearchPattern(pattern));
      }
    }

    public TrackInfo GetTrackInfo(string FileName) {
      TrackInfo info = new TrackInfo();
      info.Name = System.IO.Path.GetFileNameWithoutExtension(FileName);
      info.FileName = FileName;
      foreach (SearchPattern p in SearchPatterns) {
        if (p.FillTrackInfo(info))
          break;
      }
      return info;
    }
  }

  public class SearchPattern
  {
    /// <summary>
    /// A Regular Expression to search on. This is applied to a filename in
    /// the FillTrackInfo method.
    /// </summary>
    public string Pattern { get; set; }

    /// <summary>
    /// example: <para>Show (1_2)-episode.mpg</para>
    /// <para>Pattern=@"^(.+?) \((\d+_\d+)\)-\((.+?)\)"</para>
    /// <para>SearchFields={"Show","PartNo","PartOf","Episode"}</para>
    /// </summary>
    public string[] SearchFields {
      get;
      set;
    }

    public SearchPattern() { }
    public SearchPattern(string RegEx, string[] ReplaceFields) {
      this.Pattern = RegEx;
      this.SearchFields = ReplaceFields;
    }

    /// <summary>
    /// Create a search pattern. This expands a simple search string in to 
    /// a regular expression and a list of replacement tokens.
    /// <list type="table">
    /// <listheader>Valid values</listheader>
    /// <item>$s Show Name</item>
    /// <item>$e Episode Name</item>
    /// <item>$p Part No (1_2 becomes 1/2)</item>
    /// <item>$x Part No (the 1 in 1/2) only matches digits</item>
    /// <item>$n Parts (the 2 in 1/2) only matches digits</item>
    /// <item>$y year,  only matches digits</item>
    /// <item>$m month,  only matches digits</item>
    /// <item>$d day,  only matches digits</item>
    /// </list>
    /// </summary>
    /// <param name="SearchPattern"></param>
    public SearchPattern(string SearchPattern) {
      string tmp;

      tmp = System.Text.RegularExpressions.Regex.Escape(SearchPattern);
      tmp = tmp.Replace(@"\$s", @"(.+)");
      tmp = tmp.Replace(@"\$e", @"(.+)");
      tmp = tmp.Replace(@"\$x", @"(\d+)");
      tmp = tmp.Replace(@"\$n", @"(\d+)");
      tmp = tmp.Replace(@"\$m", @"(\d+)");
      tmp = tmp.Replace(@"\$d", @"(\d+)");
      tmp = tmp.Replace(@"\$y", @"(\d+)");
      Pattern = tmp;

      SortedList<int, string> tokens = new SortedList<int, string>();
      AddToken(tokens, SearchPattern, "$s");
      AddToken(tokens, SearchPattern, "$e");
      AddToken(tokens, SearchPattern, "$x");
      AddToken(tokens, SearchPattern, "$n");
      AddToken(tokens, SearchPattern, "$m");
      AddToken(tokens, SearchPattern, "$d");
      AddToken(tokens, SearchPattern, "$y");
      AddToken(tokens, SearchPattern, "$p");

      this.SearchFields = new string[tokens.Count];
      tokens.Values.CopyTo(SearchFields, 0);
    }

    void AddToken(SortedList<int, string> list, string text, string token) {
      int pos = text.IndexOf(token);
      if (pos == -1)
        return;

      list.Add(pos, token);
    }

    GroupCollection GetMatches(string Pattern, string Text) {
      Regex r = new System.Text.RegularExpressions.Regex(Pattern);
      MatchCollection matches = r.Matches(Text);
      if (matches.Count > 0)
        return matches[0].Groups;
      return null;
    }


    /// <summary>
    /// Attempts to set the data in a TrackInfo object based on the pattern. 
    /// If the pattern search was successful, returns true. If not, returns false
    /// and no data in info is changed.
    /// </summary>
    /// <param name="info">TrackInfo object to fill.</param>
    /// <returns>True if match was a success. False if not.</returns>
    public bool FillTrackInfo(TrackInfo info) {
      string fileName = System.IO.Path.GetFileNameWithoutExtension(info.FileName);
      GroupCollection matchGroups = GetMatches(Pattern, fileName);
      if (matchGroups != null && matchGroups.Count == SearchFields.Length + 1) {
        for (int i = 0; i < SearchFields.Length; i++) {
          SetField(info, SearchFields[i], matchGroups[i + 1].Value);
        }
        return true;
      }
      return false;
    }

    public void SetField(TrackInfo info, string FieldName, string Value) {
      Debug.WriteLine("SetField " + FieldName + "=" + Value);

      switch (FieldName) {
        case "ShowName":
        case "$s":
          info.ShowName = Value;
          if (info.Episode == "")
            info.Episode = Value;
          break;
        case "PartNo":
        case "$x":
          info.PartNo = Value;
          break;
        case "PartOf":
        case "$n":
          info.PartOf = Value;
          break;
        case "Part":
        case "$p":
          string[] parts = Value.Split('_');
          if (parts.Length == 2) {
            info.PartNo = parts[0];
            info.PartOf = parts[1];
          }
          break;
        case "Episode":
        case "$e":
          info.Episode = Value;
          if (info.ShowName == "")
            info.ShowName = Value;
          break;
        case "$y":
          info.Year = Value;
          break;
        case "$m":
          info.Month = Value;
          break;
        case "$d":
          info.Day = Value;
          break;
      }
    }
  }

}
